/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

package lime2;
import lights.adapters.*;
import java.io.*;
import java.net.*;

/** 
 * Provides diagnostics for the LimeServer on the basis of HTTP.
 * <br> 
 * It is a "lightweight" service: this is not a parallel HTTP server.
 * <br>
 * Please note this is the only class in which private methods are "javadoc" documented. 
 * 
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

public class LimeHTTP implements Runnable
{
 // private constant: HEADER	
 private static final String HEADER = "HTTP/1.1 200 OK"                                    + '\r' + '\n' + 
                                      "Server: LimeHTTP"                               + '\r' + '\n' +
						              "Connection: close"                                  + '\r' + '\n' +
						              "Content-Type: text/html"                            + '\r' + '\n' +
						              "Cache-Control: no-store, no-cache, must-revalidate" + '\r' + '\n' +
									  "Pragma: no-cache"                                   + '\r' + '\n' +
									  '\n';		
	
 // private constant: maximum number of pending clients the operating system is allowed to enqueue
 private static final int MAX_PENDING_CLIENTS = 50;
 
 // private member variable: port for this service
 private int port = -1;

 /**
  * Constructor method.
  * 
  * @param port for this service
  */
 LimeHTTP(int port)
 {
  this.port = port;
 }
 	
 /**
  * Run method.
  */
 public void run()
 {
  // initializes variables	
  ServerSocket ssock = null;	
  Socket client = null;
  BufferedInputStream bis = null;
  BufferedOutputStream bos = null;	
  
  // listens on the port for diagnostics 
  try { ssock = new ServerSocket(port, MAX_PENDING_CLIENTS); } 
  catch (IOException ioe) { throw new LimeError("can't listen on port " + port + " for http diagnostics", ioe); }
  
  // loops forever 
  for(;;) 
  {
   try 
   {
   	// accepts a new connection (does NOT accept multiple connections)
	client = ssock.accept();
    bis = new BufferedInputStream(client.getInputStream());	
    bos = new BufferedOutputStream(client.getOutputStream());

    // CORE
    String URL = getRequest(bis);   
    sendResponse(processRequest(URL), bos);
       
    // closes connection
    bos.flush();
    bos.close();
    bis.close(); 
    client.close(); 
   }
   catch (IOException ioe) 
   { 
   	// we don't want an error in the HTTP server to halt the Lime middleware
   	System.out.println("LimeHTTP: I/O error on socket"); 
    ioe.printStackTrace();
   }
  } // end: for (;;)
  
 } 
  
 /**
  * Returns the page requested by the browser.
  * 
  * @param input stream 
  * @return <code>null</code> if the request of the browser has invalid syntax. Otherwise returns the URL in the 
  *         format <code>/URL</code>, i.e. "/page1.html", "/section/doc.html" , etc..
  *         <br>
  *         Many browsers request <code>/<code> for the index file of a website
  * @throws IOException
  */ 
 private static String getRequest(BufferedInputStream bis) throws IOException
 {
  String msg = new String("");
  boolean flag = true;
  
  while (flag) 
  { 
   int c = bis.read(); 
   if (c == -1 || c == '\r' || c == '\n') flag = false; else msg = msg + (char) c;  
  }
 
  if (!msg.startsWith("GET /")) return null; 
  if (!(msg.endsWith(" HTTP/1.1") || msg.endsWith(" HTTP/1.0") || msg.endsWith(" HTTP/0.9"))) return null;
  
  return msg.substring(msg.indexOf('/'), msg.lastIndexOf(' '));
 }
 
 /**
  * Returns the webpage (plain HTML) corresponding to a given URL. Please note that trying to generate a LimeServer
  * error page when an internal error occurs is hardly possible: Lime internals propagate errors/exceptions towards
  * the LocalOpMgr (thread) only.
  * 
  * @param URL - the URL of the webpage that the browser is asking for 
  * @return plain HTML of the webpage
  */
 private static String processRequest(String URL)
 {
  // is the request invalid ?
  if (URL == null) return generateBad();	
  // is URL the index page ?
  if (URL.equals("/") || URL.equals("/index.html")) return generateIndex();	
  // is URL the tuplespaces page ?
  if (URL.equals("/tuplespaces.html")) return generateTuplespaces();
  // is URL the LimeSystemTupleSpace page ?
  if (URL.equals("/" + LimeConstants.SYSTEM_NAME + ".html")) return generateSystem();
  // last chance: is URL the page of a specific tuplespace ? if not, this method must generate a bad response
  return generateTuplespaceOrBad(URL);
 }
 
 /**
  * Sends the webpage to the browser.
  * 
  * @param HTML webpage
  * @param bos output stream
  * @throws IOException
  */
 private static void sendResponse(String HTML, BufferedOutputStream bos) throws IOException
 {
  HTML = HEADER + HTML; 
  for (int i = 0; i < HTML.length(); i++) bos.write(HTML.charAt(i));
 }
  
 // generates index page
 private static String generateIndex()
 {
  return   "<html>"
  + "\n" 
  + "\n" + "<head>"
  + "\n" + "<meta http-equiv=\"Content-Language\" content=\"en-us\">"
  + "\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">" 
  + "\n" + "<title>Lime II</title>"
  + "\n" + "</head>"
  + "\n" 
  +	"\n" + "<body>"
  + "\n"  
  + "\n" + "<h1 align=\"center\"><font color=\"#FF6600\" face=\"Lucida Console\">"
  + "\n" + "<span style=\"letter-spacing: 4pt; font-weight: 400\">Http: </span></font><font color=\"#00FF00\" face=\"Impact\" size=\"7\">Lime II"
  + "\n" + "</font><font color=\"#00FF00\" face=\"Comic Sans MS\" size=\"7\">*</font><font size=\"2\" color=\"#808080\"><span style=\"font-weight: 400; vertical-align: sub\">"
  + "\n" + "by G.P. Picco, A. Murphy, L. Bellini</span></font></h1>"
  + "\n" + "<hr>"
  + "\n" + "<p align=\"left\"><b><font face=\"Courier New\" size=\"5\">Diagnostics Service</font></b></p>"
  + "\n" + "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"799\" id=\"AutoNumber1\">"
  +	"\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Unicast address:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getUnicastAddress().getHostName() + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Unicast port:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getUnicastPort() + "</font></i></td>"
  + "\n" + "</tr>"
  +	"\n" + "<tr>"                                        
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Multicast address:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getMulticastAddress() + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Multicast port:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getMulticastPort()+ "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Load port:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getLoadPort() + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">HTTP port:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getDiagnosticsPort() + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">LighTS factory:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getTSFactory() + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">C-Adapter:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + LimeServer.getServer().getCAdapter() + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Java runtime:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + System.getProperty("java.version") + " " + System.getProperty("java.vendor") + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Operating system:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + System.getProperty("os.name") + " " + System.getProperty("os.version") + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Architecture:</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + System.getProperty("os.arch") + "</font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\">&nbsp;</td>"
  + "\n" + "<td width=\"681\">&nbsp;</td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Memory (total):</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + Runtime.getRuntime().totalMemory() + " bytes </font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Memory (free):</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + Runtime.getRuntime().freeMemory() + " bytes </font></i></td>"
  + "\n" + "</tr>"
  + "\n" + "<tr>"
  + "\n" + "<td width=\"118\"><font face=\"Arial Narrow\">Memory (used):</font></td>"
  + "\n" + "<td width=\"681\"><i><font face=\"Arial Narrow\">" + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + " bytes </font></i></td>"
  + "\n" + "</tr>"
  +	"\n" + "</table>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Inspect: "
  + "\n" + "<a href=\"tuplespaces.html\" style=\"text-decoration: none\">tuplespaces</a></font></p>"  
  + "\n" + "<hr>"
  + "\n" 
  + "\n" + "</body>"
  + "\n" 
  + "\n" + "</html>";
 }
 
 // generates tuplespaces page
 private static String generateTuplespaces() 
 {
  String[] tps = LimeTupleSpace.getNames();	
    
  String GEN = 
    "\n" + "<tr>" 
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; " 
         + "<a href=\"" + LimeConstants.SYSTEM_NAME + ".html\" style=\"text-decoration: none\">" + LimeConstants.SYSTEM_NAME + "</a></font></td>"
  + "\n" + "</tr>";	
  for (int i = 0; i < tps.length; i++) GEN = GEN +
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; " 
         + "<a href=\"" + tps[i] + ".html\" style=\"text-decoration: none\">" + tps[i] + "</a></font></td>"
  + "\n" + "</tr>";	
  
  return   "<html>"
  + "\n" 
  + "\n" + "<head>"
  + "\n" + "<meta http-equiv=\"Content-Language\" content=\"en-us\">"
  + "\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">" 
  + "\n" + "<title>Lime II</title>"
  + "\n" + "</head>"
  + "\n" 
  +	"\n" + "<body>"
  + "\n"  
  + "\n" + "<h1 align=\"center\"><font color=\"#FF6600\" face=\"Lucida Console\">"
  + "\n" + "<span style=\"letter-spacing: 4pt; font-weight: 400\">Http: </span></font><font color=\"#00FF00\" face=\"Impact\" size=\"7\">Lime"
  + "\n" + "</font><font color=\"#00FF00\" face=\"Comic Sans MS\" size=\"7\">*</font><font size=\"2\" color=\"#808080\"><span style=\"font-weight: 400; vertical-align: sub\">"
  + "\n" + "by G.P. Picco, A. Murphy, L. Bellini</span></font></h1>"
  + "\n" + "<hr>"
  + "\n" + "<p align=\"left\"><b><font face=\"Courier New\" size=\"5\">Diagnostics Service</font></b></p>"	
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Tuplespaces on LimeServer " + LimeServer.getServer().getServerID() + "</font></p>"
  + "\n" + "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"100%\" id=\"AutoNumber2\">"
  + GEN
  + "\n" + "</table>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Goto: "
  + "\n" + "<a href=\"index.html\" style=\"text-decoration: none\">index</a></font></p>"
  + "\n" + "<hr>"
  + "\n" 
  + "\n" + "</body>"
  + "\n" 
  + "\n" + "</html>";
 }
 
 // tries to generate the page of the LimeSystemTupleSpace
 private static String generateSystem() 
 {   
  LimeTemplate allhere = new LimeTemplate(Location.HERE, Location.HERE, new Tuple());
  LimeTuple[] lt = LimeSystemTupleSpace.get().rdg(allhere);
  String GEN_TUPLES = new String("");
   
  if ((lt == null) || (lt != null && lt.length == 0)) GEN_TUPLES =
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; none</font></td>"
  + "\n" + "</tr>";	
  else for (int i = 0; i < lt.length; i++) GEN_TUPLES = GEN_TUPLES +
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; " + toFineString2nd(lt[i]) + "</font></td>"
  + "\n" + "</tr>";	
 
  String[] sr = LimeSystemTupleSpace.get().getTextualRegisteredReactions();
  String GEN_REACTIONS = new String("");
  
  if ((sr == null) || (sr != null && sr.length == 0)) GEN_REACTIONS =
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; none</font></td>"
  + "\n" + "</tr>";	
  else for (int i = 0; i < sr.length; i++) GEN_REACTIONS = GEN_REACTIONS +
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; " + sr[i] + "</font></td>"
  + "\n" + "</tr>";	
    
  return   "<html>"
  + "\n" 
  + "\n" + "<head>"
  + "\n" + "<meta http-equiv=\"Content-Language\" content=\"en-us\">"
  + "\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">" 
  + "\n" + "<title>Lime II</title>"
  + "\n" + "</head>"
  + "\n" 
  +	"\n" + "<body>"
  + "\n"  
  + "\n" + "<h1 align=\"center\"><font color=\"#FF6600\" face=\"Lucida Console\">"
  + "\n" + "<span style=\"letter-spacing: 4pt; font-weight: 400\">Http: </span></font><font color=\"#00FF00\" face=\"Impact\" size=\"7\">Lime"
  + "\n" + "</font><font color=\"#00FF00\" face=\"Comic Sans MS\" size=\"7\">*</font><font size=\"2\" color=\"#808080\"><span style=\"font-weight: 400; vertical-align: sub\">"
  + "\n" + "by G.P. Picco, A. Murphy, L. Bellini</span></font></h1>"
  + "\n" + "<hr>"
  + "\n" + "<p align=\"left\"><b><font face=\"Courier New\" size=\"5\">Diagnostics Service</font></b></p>"	
  + "\n" + "<p align=\"left\"><font face=\"Courier New\"> " + LimeConstants.SYSTEM_NAME + "<font face=\"Arial\"> @ </font>" + LimeServer.getServer().getServerID() + "</font></p>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Resident tuples:</font></p>"
  + "\n" + "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"100%\" id=\"AutoNumber2\">"
  + GEN_TUPLES
  + "\n" + "</table>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Installed reactions:</font></p>"
  + "\n" + "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"100%\" id=\"AutoNumber2\">"
  + GEN_REACTIONS
  + "\n" + "</table>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Goto: "
  + "\n" + "<a href=\"tuplespaces.html\" style=\"text-decoration: none\">tuplespaces</a></font></p>"
  + "\n" + "<hr>"
  + "\n" 
  + "\n" + "</body>"
  + "\n" 
  + "\n" + "</html>";  
 }
 
 // tries to generate the page of a specific tuplespace; if it is not possible, then generates a bad response
 private static String generateTuplespaceOrBad(String URL) 
 {
  String[] names = LimeTupleSpace.getNames();	
  
  boolean flag = true;
  int z = 0;
  while (flag && z < names.length) if (URL.equals("/" + names[z] + ".html")) flag = false; else z++;
  
  if (flag) return generateBad();
   
  LimeTemplate allhere = new LimeTemplate(Location.HERE, Location.UNSPECIFIED, new Tuple());
  LimeTuple[] lt = LimeTupleSpace.get(names[z]).rdg(allhere);
  String GEN_TUPLES = new String("");
   
  if ((lt == null) || (lt != null && lt.length == 0)) GEN_TUPLES =
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; none</font></td>"
  + "\n" + "</tr>";	
  else for (int i = 0; i < lt.length; i++) GEN_TUPLES = GEN_TUPLES +
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; " 
         + toFineString(lt[i])
         + " for " + (lt[i].getDestination().isHere() ? "this server" : lt[i].getDestination().toString())  
		 + "</font></td>"
  + "\n" + "</tr>";	
 
  String[] sr = LimeTupleSpace.get(names[z]).getTextualRegisteredReactions();
  String GEN_REACTIONS = new String("");
  
  if ((sr == null) || (sr != null && sr.length == 0)) GEN_REACTIONS =
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; none</font></td>"
  + "\n" + "</tr>";	
  else for (int i = 0; i < sr.length; i++) GEN_REACTIONS = GEN_REACTIONS +
    "\n" + "<tr>"
  + "\n" + "<td width=\"100%\"><font face=\"Times New Roman\">&nbsp &#9675; " + sr[i] + "</font></td>"
  + "\n" + "</tr>";	
    
  return   "<html>"
  + "\n" 
  + "\n" + "<head>"
  + "\n" + "<meta http-equiv=\"Content-Language\" content=\"en-us\">"
  + "\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">" 
  + "\n" + "<title>Lime II</title>"
  + "\n" + "</head>"
  + "\n" 
  +	"\n" + "<body>"
  + "\n"  
  + "\n" + "<h1 align=\"center\"><font color=\"#FF6600\" face=\"Lucida Console\">"
  + "\n" + "<span style=\"letter-spacing: 4pt; font-weight: 400\">Http: </span></font><font color=\"#00FF00\" face=\"Impact\" size=\"7\">Lime"
  + "\n" + "</font><font color=\"#00FF00\" face=\"Comic Sans MS\" size=\"7\">*</font><font size=\"2\" color=\"#808080\"><span style=\"font-weight: 400; vertical-align: sub\">"
  + "\n" + "by G.P. Picco, A. Murphy, L. Bellini</span></font></h1>"
  + "\n" + "<hr>"
  + "\n" + "<p align=\"left\"><b><font face=\"Courier New\" size=\"5\">Diagnostics Service</font></b></p>"	
  + "\n" + "<p align=\"left\"><font face=\"Courier New\"> " + names[z] + "<font face=\"Arial\"> @ </font>" + LimeServer.getServer().getServerID() + "</font></p>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Resident tuples:</font></p>"
  + "\n" + "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"100%\" id=\"AutoNumber2\">"
  + GEN_TUPLES
  + "\n" + "</table>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Installed reactions:</font></p>"
  + "\n" + "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse: collapse\" bordercolor=\"#111111\" width=\"100%\" id=\"AutoNumber2\">"
  + GEN_REACTIONS
  + "\n" + "</table>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Goto: "
  + "\n" + "<a href=\"tuplespaces.html\" style=\"text-decoration: none\">tuplespaces</a></font></p>"
  + "\n" + "<hr>"
  + "\n" 
  + "\n" + "</body>"
  + "\n" 
  + "\n" + "</html>";  
 }
 
 // generates response to bad request 
 private static String generateBad()
 {
  return   "<html>"
  + "\n" 
  + "\n" + "<head>"
  + "\n" + "<meta http-equiv=\"Content-Language\" content=\"en-us\">"
  + "\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">" 
  + "\n" + "<title>Lime II</title>"
  + "\n" + "</head>"
  + "\n" 
  +	"\n" + "<body>"
  + "\n"  
  + "\n" + "<h1 align=\"center\"><font color=\"#FF6600\" face=\"Lucida Console\">"
  + "\n" + "<span style=\"letter-spacing: 4pt; font-weight: 400\">Http: </span></font><font color=\"#00FF00\" face=\"Impact\" size=\"7\">Lime"
  + "\n" + "</font><font color=\"#00FF00\" face=\"Comic Sans MS\" size=\"7\">*</font><font size=\"2\" color=\"#808080\"><span style=\"font-weight: 400; vertical-align: sub\">"
  + "\n" + "by G.P. Picco, A. Murphy, L. Bellini</span></font></h1>"
  + "\n" + "<hr>"
  + "\n" + "<p align=\"left\"><b><font face=\"Courier New\" size=\"5\">BAD REQUEST</font></b></p>"
  + "\n" + "<p align=\"left\"><font face=\"Arial Narrow\">Goto: "
  + "\n" + "<a href=\"index.html\" style=\"text-decoration: none\">index</a></font></p>"
  + "\n" + "<hr>"
  + "\n" 
  + "\n" + "</body>"
  + "\n" 
  + "\n" + "</html>";
 }
 
 /**
  * Given a LimeTuple returns a fine text/html representation.
  * 
  * @param ft LimeTuple
  * @return textual representation
  */
 static String toFineString(LimeTuple ft)
 {
  return ft.getTuple().toString().replace('<', '{').replace('>', '}') + " [" + ft.getID() + "]";	
 }
 
 /**
  * Given a LimeTuple returns an alternative fine text/html representation.
  * 
  * @param ft LimeTuple
  * @return textual representation
  */
 static String toFineString2nd(LimeTuple ft)
 {
  return ft.getTuple().toString().replace('<', '{').replace('>', '}'); 	
 }
 
 /**
  * Given a Reaction returns a fine text/html representation.
  * 
  * @param fr Reaction
  * @return textual representation
  */
 static String toFineString(RegisteredReaction fr)
 {
  return (fr.getListener().getClass().getName()) +
         " [" + (fr.getReactionID().toString()) + "], " + 
		 (fr.getReactionID().isPureStrong() ? "strong, " : "weak, ") +
         (fr.getMode() == Reaction.ONCE ? "once, " : "once-per-tuple, ") + 
         fr.getLimeTemplate().getTuple().toString().replace('<', '{').replace('>', '}');	
 }
 
}
